<?php
/**
 * BaseController.php
 * @copyright (c) 2011-2020, Hangzhou DOJI Tech Co., Ltd.
 * This is NOT a freeware, use is subject to license terms.
 * @package BaseController.php
 * @link https://www.tingjunapp.com/
 * @author wujun@lanqb.cn
 * @: BaseController.php 310000 2020-11-06 15:23:53 $
 * */

namespace App\Http\Controllers;

use App\Common\Libs\ArrayHelper;
use App\Common\Libs\IdBuilder;
use App\Consts\ExceptionCode;
use App\Exceptions\Error;
use App\Models\Cache\Cache;
use App\Models\Cache\CacheKey;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class BaseController extends Controller
{

    /** @var array $validate 参数验证器 */
    public $validate = [];

    /** @var User|object $user */
    public $user = '';

    public $isTourist = false;

    /**
     * @var string $token
     */
    public $token = '';

    /** @var $error */
    private $error;

    /**
     * init controller before action
     * @return mixed
     */
    /**
     * @param array $parameters
     * @return bool
     * @throws \Exception
     */
    public function init($parameters = [])
    {
        // @INIT Implement __INIT() method.
        $parameters = collect($parameters)->toArray();
        foreach ($parameters as $parameter) {
            foreach ($parameter as $key => $value) {
                $this->{$key} = $value;
            }
        }
        return true;
    }

    /**
     * before action
     * @param array $parameters
     * @return mixed
     * @throws \Exception
     */
    public function beforeAction($parameters = [])
    {
        // validate request
        $this->validate(\request(), $this->validate);
        // init
        return $this->init($parameters);
    }

    /**
     * after action
     * @param array $parameters
     * @return mixed
     */
    public function afterAction($parameters = [])
    {
        return true;
    }

    /**
     * load data
     * @param array $data
     * @return $this
     */
    public function load($data)
    {
        foreach ($data as $prop => $datum) {
            $this->$prop = $datum;
        }
        return $this;
    }

    /**
     * @param string $method
     * @param array $parameters
     * @return \Symfony\Component\HttpFoundation\Response|bool
     */
    public function callAction($method, $parameters)
    {
        try {
            $this->beforeAction($parameters);
            if ($this->error) {
                return $this->error;
            }
            // 去除 request 中间件 多余参数
            if (isset($parameters[0]['user']) && isset($parameters[0]['token'])) {
                unset($parameters[0]['user']);
                unset($parameters[0]['token']);
            }

            //如果接收过来的是 null 转成空字符串
            if (!empty($parameters)) {
                foreach ($parameters[0]->request->all() as $key => $parameter) {
                    if ($parameter === null) {
                        $parameters[0]->request->set($key, '');
                    }
                }
            }

            $result = parent::callAction($method, $parameters);
            $this->afterAction($parameters);
            return $result;
        } catch (\Exception $e) {
            $msg = '';
            $code = $e->getCode();
            if (empty($code) && '#' === substr($e->getMessage(), 0, 1)) {
                $code = explode(' - ', $e->getMessage(), 2);
                $msg = !empty($code[1]) ? $code[1] : false;
                $code = intval(substr($code[0] ?? '', 1));
            }
            $code = is_numeric($code) && $code ? $code : ExceptionCode::CODE_FAIL;
            // prod 环境屏蔽错误信息
            if (!isProd() && !strpos($e->getFile(), 'BaseController')) {
                $msg = false === $msg ? '' : ($msg ?: $e->getMessage());
                if ($msg && false === strpos($msg, ' @file[ ')) {
                    $msg .= ' @file[ ' . $e->getFile() . ' : ' . $e->getLine() . ' ]. ';
                }
            }
            return $this->apiReturn($msg, $code);
        }
    }

    /**
     * get property
     * @param $property
     * @return |null
     */
    public function __get($property)
    {
        if (isset($this->$property)) {
            return $this->$property;
        } else {
            return null;
        }
    }

    /**
     * set property
     * @param $name
     * @param $value
     * @return mixed
     */
    public function __set($name, $value)
    {
        return $this->$name = $value;
    }

    /**
     * 自动验证接口参数
     * @param Request $request
     * @param array $rules
     * @param array $messages
     * @param array $customAttributes
     * @return \Illuminate\Http\JsonResponse|bool
     */
    public function validate(Request $request, array $rules, array $messages = [], array $customAttributes = [])
    {
        $method = $request->route()->getActionMethod();
        if (isset($rules[$method]['rules'])) {
            $messages = $rules[$method]['messages'] ?? [];
            $rules = $rules[$method]['rules'];
        }
        if (empty($rules)) return true;
        $validator = Validator::make($request->all(), $rules, $messages, $customAttributes);
        if ($validator->fails()) {
            $validatorMsg = 'validation.' . $validator->errors()->first();
            $errorMsg = $interrupt = lang($validatorMsg);
            if ($interrupt == $validatorMsg) {
                $errorMsg = '';
                // prod 环境屏蔽未定义 validator 错误信息
                if (isProd()) {
                    $interrupt = true;
                }
            } else {
                $interrupt = true;
            }
            return $this->error = Error::handle($errorMsg, ExceptionCode::ILLEGAL_PARAMETER, $interrupt);
        }
        return true;
    }

    /**
     * 获取当前登录的用户ID
     * @return array|string|null
     */
    public function getCurrentUserId()
    {
        if ($this->user && isset($this->user->user_id)) {
            return $this->user->user_id;
        }

        // 游客ID
        $this->isTourist = true;
        $touristId = Cache::getInstance(['token' => $this->token])
            ->get(CacheKey::TOKEN_TOURIST_SNAPSHOT);
        return $touristId ?? IdBuilder::getUniqueID();
    }

    /**
     * 获取基本信息
     * @param array $params
     * @param array $extends
     * @return array
     */
    public function getBaseInfo($params = [], $extends = [])
    {
        $params = $params ?: [];
        $params['token'] = $this->token;
        $params['user_id'] = array_key_exists('user_id', $params) ? $params['user_id'] : $this->getCurrentUserId();
        $params['current_user_id'] = $this->getCurrentUserId() ?: $params['user_id'];
        $params = ArrayHelper::remove($params, ['sign', 'timestamp']);

        return ArrayHelper::merge($params, $extends);
    }

    /**
     * 过滤签名参数
     * @param array $params
     * @return mixed|null
     */
    public function filterSignParams($params = [])
    {
        $params = $params ?: [];
        return ArrayHelper::remove($params, ['sign', 'timestamp']);
    }

    /**
     * 接口统一返回
     * api result return
     * [!] 与 Error::handle 区别：
     *     - apiReturn     注重返回，可以处理Error返回结果
     *     - Error::handle 注重错误处理
     * [!] 重定向支持，append： $data['__REDIRECT_URL'] = 'https://www.baidu.com';
     * [!] Cookie支持，append： $data['__COOKIES'] = ['token' => 'UUID', 'uid' => '00XP'];
     * @param mixed $data
     * @param int $code
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    public function apiReturn($data = [], $code = 200, $message = '')
    {
        return jsonReturn($data, $code, $message);
    }

}
